package com.eian.boot.module.system.auth.service;

import com.eian.boot.common.core.enums.CommonEnum;
import com.eian.boot.common.core.exception.ExceptionHelper;
import com.eian.boot.common.core.model.model.LoginUser;
import com.eian.boot.common.core.utils.BeanCopierUtils;
import com.eian.boot.common.core.utils.IpUtils;
import com.eian.boot.common.enums.LoginStatusEnum;
import com.eian.boot.common.enums.LoginTypeEnum;
import com.eian.boot.common.enums.PermissionTypeEnum;
import com.eian.boot.module.system.associations.user.dept.service.UserDeptRelationService;
import com.eian.boot.module.system.auth.model.dto.LoginRequest;
import com.eian.boot.module.system.dept.model.entity.SysDeptEntity;
import com.eian.boot.module.system.dept.service.SysDeptService;
import com.eian.boot.module.system.log.model.dto.LoginLogDTO;
import com.eian.boot.module.system.log.service.SysLoginLogService;
import com.eian.boot.module.system.permission.model.dto.PermissionCheckRequest;
import com.eian.boot.module.system.permission.model.entity.SysPermissionEntity;
import com.eian.boot.module.system.permission.model.vo.PermissionTreeVO;
import com.eian.boot.module.system.permission.service.PermissionCacheService;
import com.eian.boot.module.system.permission.service.SysPermissionService;
import com.eian.boot.module.system.role.model.entity.SysRoleEntity;
import com.eian.boot.module.system.role.model.vo.RoleVO;
import com.eian.boot.module.system.role.service.SysRoleService;
import com.eian.boot.module.system.user.model.entity.SysUserEntity;
import com.eian.boot.module.system.user.model.vo.UserLoginResultVO;
import com.eian.boot.module.system.user.service.SysUserService;
import com.eian.boot.security.utils.PwdKit;
import com.eian.boot.security.utils.SessionHelper;
import jakarta.validation.Valid;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

import static com.eian.boot.common.constants.ErrorMessage.Auth;
import static com.eian.boot.table.Tables.*;

@Service
@AllArgsConstructor
public class AuthService {
    private final SysUserService userService;
    private final SysRoleService roleService;
    private final SysPermissionService permissionService;
    private final PermissionCacheService cacheService;
    private final SysLoginLogService loginLogService;
    private final UserDeptRelationService userDeptRelationService;
    private final SysDeptService deptService;

    /**
     * @author alex.meng
     * @description 用户登录
     **/
    public UserLoginResultVO login(@Valid LoginRequest params) {
        String clientIp = getClientIp();
        try {
            SysUserEntity user = userService.queryChain()
                    .from(sys_user)
                    .where(sys_user.username.eq(params.getAccount())
                            .or(sys_user.email.eq(params.getAccount()))
                            .or(sys_user.telephone.eq(params.getAccount()))
                    )
                    .one();
            ExceptionHelper.ifNull(user, Auth.LOGIN_FAILED);
            if (!PwdKit.match(params.getPassword(), user.getPassword())) {
                ExceptionHelper.error(Auth.LOGIN_FAILED);
            }

            ExceptionHelper.ifTrue(user.getStatus().equals(CommonEnum.DISABLED.getValue()), Auth.ACCOUNT_DISABLED);

            LoginUser loginUser = BeanCopierUtils.copy(user, LoginUser.class);

            List<SysRoleEntity> userRoles = roleService.getUserRoles(user.getId());
            loginUser.setRolePermission(userRoles.stream().map(SysRoleEntity::getCode).collect(java.util.stream.Collectors.toSet()));

            List<String> userPerms = getUserAllPerms(user.getId());
            loginUser.setResourcePermission(new java.util.HashSet<>(userPerms));

            // 填充主部门信息
            Long mainDeptId = userDeptRelationService.getMainDeptIdByUserId(user.getId());
            if (mainDeptId != null) {
                SysDeptEntity dept = deptService.getById(mainDeptId);
                if (dept != null) {
                    loginUser.setDeptId(mainDeptId);
                    loginUser.setDeptName(dept.getDeptName());
                }
            }

            String token = SessionHelper.login(loginUser);

            user.setLastLoginTime(LocalDateTime.now());
            user.setLastLoginIp(clientIp);
            userService.updateById(user);

            // 记录登录成功日志
            loginLogService.recordLoginLog(LoginLogDTO.builder()
                    .userId(user.getId())
                    .username(user.getUsername())
                    .loginType(LoginTypeEnum.PASSWORD.getValue())
                    .loginStatus(LoginStatusEnum.SUCCESS.getValue())
                    .loginIp(clientIp)
                    .token(token)
                    .msg("登录成功")
                    .build());

            UserLoginResultVO loginResult = BeanCopierUtils.copy(user, UserLoginResultVO.class);
            loginResult.setToken(token);
            return loginResult;
        } catch (Exception e) {
            // 记录登录失败日志
            loginLogService.recordLoginLog(LoginLogDTO.builder()
                    .username(params.getAccount())
                    .loginType(LoginTypeEnum.PASSWORD.getValue())
                    .loginStatus(LoginStatusEnum.FAIL.getValue())
                    .loginIp(clientIp)
                    .msg(e.getMessage() != null ? e.getMessage() : "登录失败")
                    .build());
            throw e;
        }
    }

    private String getClientIp() {
        return Optional.ofNullable(RequestContextHolder.getRequestAttributes())
                .filter(ServletRequestAttributes.class::isInstance)
                .map(ServletRequestAttributes.class::cast)
                .map(ServletRequestAttributes::getRequest)
                .map(IpUtils::getClientIp)
                .orElse("unknown");
    }

    /**
     * @author alex.meng
     * @description 退出登录
     **/
    public void logout() {
        SessionHelper.logout();
    }

    /**
     * @author alex.meng
     * @description 获取用户全部权限（非菜单权限）
     **/
    public List<String> getUserAllPerms(Long userId) {
        return permissionService.queryChain()
                .select(sys_permission.code)
                .from(sys_permission)
                .innerJoin(sys_role_permission).on(sys_role_permission.permission_id.eq(sys_permission.id))
                .innerJoin(sys_role).on(sys_role_permission.role_id.eq(sys_role.id))
                .innerJoin(sys_user_role).on(sys_user_role.role_id.eq(sys_role.id))
                .where(sys_user_role.user_id.eq(userId)
                        .and(sys_permission.type.eq(PermissionTypeEnum.BUTTON.getValue()))
                        .and(sys_role.status.eq(CommonEnum.ENABLED.getValue())))
                .listAs(String.class);
    }

    /**
     * @author alex.meng
     * @description 检查当前用户权限有效性
     **/
    public boolean permissionCheck(PermissionCheckRequest params) {
        Long userId = SessionHelper.getUserId();

        long count = permissionService.queryChain()
                .select(sys_permission.code)
                .from(sys_permission)
                .innerJoin(sys_role_permission).on(sys_role_permission.permission_id.eq(sys_permission.id))
                .innerJoin(sys_role).on(sys_role_permission.role_id.eq(sys_role.id))
                .innerJoin(sys_user_role).on(sys_user_role.role_id.eq(sys_role.id))
                .where(sys_user_role.user_id.eq(userId)
                        .and(sys_permission.code.in(params.getPermissions()))
                        .and(sys_permission.type.eq(PermissionTypeEnum.BUTTON.getValue()))
                        .and(sys_role.status.eq(CommonEnum.ENABLED.getValue())))
                .count();

        if (params.getAll() != null && params.getAll()) {
            return count == params.getPermissions().size();
        } else {
            return count > 0;
        }
    }

    /**
     * @author alex.meng
     * @description 获取用户菜单树
     **/
    public List<PermissionTreeVO> getUserMenuTree(Long userId) {
        List<PermissionTreeVO> cached = cacheService.getUserMenus(userId);
        if (cached != null && !cached.isEmpty()) {
            return cached;
        }

        List<SysPermissionEntity> userMenus = permissionService.queryChain()
                .from(sys_permission)
                .innerJoin(sys_role_permission).on(sys_role_permission.permission_id.eq(sys_permission.id))
                .innerJoin(sys_role).on(sys_role_permission.role_id.eq(sys_role.id))
                .innerJoin(sys_user_role).on(sys_user_role.role_id.eq(sys_role.id))
                .where(sys_user_role.user_id.eq(userId)
                        .and(sys_permission.type.in(PermissionTypeEnum.CATALOG.getValue(), PermissionTypeEnum.MENU.getValue()))
                        .and(sys_permission.status.eq(CommonEnum.ENABLED.getValue()))
                        .and(sys_role.status.eq(CommonEnum.ENABLED.getValue())))
                .orderBy(sys_permission.sort_order, true)
                .orderBy(sys_permission.id, true)
                .list();

        List<PermissionTreeVO> menuTree = buildMenuTree(userMenus, 0L);

        if (!menuTree.isEmpty()) {
            cacheService.setUserMenus(userId, menuTree);
        }

        return menuTree;
    }

    private List<PermissionTreeVO> buildMenuTree(List<SysPermissionEntity> allMenus, Long parentId) {
        List<PermissionTreeVO> result = new ArrayList<>();
        Map<Long, List<SysPermissionEntity>> menuGroup = allMenus.stream()
                .collect(Collectors.groupingBy(menu -> menu.getParentId() == null ? 0L : menu.getParentId()));

        List<SysPermissionEntity> currentLevelMenus = menuGroup.get(parentId);
        if (currentLevelMenus != null) {
            for (SysPermissionEntity menu : currentLevelMenus) {
                PermissionTreeVO tmp = com.eian.boot.common.core.utils.BeanCopierUtils.copy(menu, PermissionTreeVO.class);
                // 手动映射name到title字段(Entity和VO字段名不一致)
                tmp.setTitle(menu.getName());
                List<PermissionTreeVO> children = buildMenuTree(allMenus, menu.getId());
                tmp.setChildren(children);
                result.add(tmp);
            }
        }
        return result;
    }

    /**
     * @author alex.meng
     * @description 获取用户角色列表
     **/
    public List<RoleVO> getUserRoleList(Long userId) {
        return roleService.queryChain()
                .from(sys_role)
                .leftJoin(sys_user_role).on(sys_role.id.eq(sys_user_role.role_id))
                .where(sys_user_role.user_id.eq(userId))
                .listAs(RoleVO.class);
    }
}
